package com.tree.common.util;

import com.tree.common.exception.BizException;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.DefaultClaims;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @ClassName com.demo.common.util
 * JWT校验工具类
 * <ol>
 * <li>iss: jwt签发者</li>
 * <li>sub: jwt所面向的用户</li>
 * <li>aud: 接收jwt的一方</li>
 * <li>exp: jwt的过期时间，这个过期时间必须要大于签发时间</li>
 * <li>nbf: 定义在什么时间之前，该jwt都是不可用的</li>
 * <li>iat: jwt的签发时间</li>
 * <li>jti: jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击</li>
 * </ol>
 * @Author tree
 * @Date 2019-05-26 14:11
 * @Version 1.0
 */
public class JwtUtil2 {
    private static final Logger log = LoggerFactory.getLogger(JwtUtil2.class);
    public static final String JWT_AUTHORIZATION = "authorization";

    /**
     * JWT 加解密类型
     */
    private static final SignatureAlgorithm JWT_ALG = SignatureAlgorithm.HS256;
    /**
     * JWT 生成密钥使用的密码
     */
    private static final String JWT_RULE = "spring_boot_demo";

    /**
     * JWT 添加至HTTP HEAD中的前缀
     */
    private static final String JWT_SEPARATOR = "Bearer ";

    private static long ttlMillis = 15*1000;

    public static SecretKey generateKey(){
        byte[] encodeKey = Base64.encodeBase64(JWT_RULE.getBytes());
        SecretKey key = new SecretKeySpec(encodeKey,0,encodeKey.length,"AES");
        return key;
    }


    public static String buildJWT(Map<String,Object> kv){
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        SecretKey key = generateKey();
        Claims claims = new DefaultClaims();
        claims.setId(UUID.randomUUID().toString());
        claims.setIssuedAt(now);
        claims.putAll(kv);
        if(ttlMillis >= 0){
            long expMillis  = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            claims.setExpiration(exp);
        }
        JwtBuilder builder = Jwts.builder().setClaims(claims).signWith(JWT_ALG,key);

        return JWT_SEPARATOR + builder.compact();
    }

    public static String refreshExpireDate(Claims claims){
        SecretKey key = generateKey();
        long expireMillis = System.currentTimeMillis()+ttlMillis;
        Date exp = new Date(expireMillis);
        claims.setExpiration(exp);

        JwtBuilder builder = Jwts.builder().setClaims(claims).signWith(JWT_ALG,key);
        return JWT_SEPARATOR + builder.compact();
    }

    private static Claims parseJWT(String jwt){
        // 移除 JWT 前的"Bearer "字符串
        jwt = StringUtils.substringAfter(jwt, JWT_SEPARATOR);
        SecretKey key = generateKey();
        Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt).getBody();

        return claims;
    }

    public static Claims getClaims(HttpServletRequest request){
        final String authHeader = request.getHeader(JWT_AUTHORIZATION);
        if(authHeader == null || !authHeader.startsWith(JWT_AUTHORIZATION)){
            log.error(Log.op("getClaims").msg("auth header is illegal or empty").toString());
            throw new BizException("登录信息为空");
        }

        return parseJWT(authHeader);
    }

    public static void main(String[] args) throws Exception{
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS");

        Map<String,Object> map = new HashMap<>(10);
        map.put("userId", "1");
        String jwt = buildJWT(map);
        System.err.println("========="+jwt+", date = " + sdf.format( new Date()));

        Thread.sleep(10000);
        Claims claims = parseJWT(jwt);
        String date = sdf.format( claims.getExpiration());
        System.err.println("========="+claims.get("userId")+", date = "+date);

        Thread.sleep(10000);
        claims = parseJWT(jwt);
        date = sdf.format( claims.getExpiration());
        System.err.println("========="+claims.get("userId")+", date = "+date);
        System.err.println("=========" + sdf.format( new Date()));

    }

}
